﻿using System;
using System.IO;

namespace myFileWatcher
{

    public class FileWatcher
    {
        private string watchPath; // 监视路径
        private int prefixLength = 0; // 存储前缀长度的值
        private Action<FileEvent> eventCallback = null;

        // 创建一个文件系统监视器
        public FileSystemWatcher Create(string path, Action<FileEvent> onEvent, Action<ErrorEventArgs> onError)
        {

            watchPath = path;
            prefixLength = watchPath.Length + 1;
            eventCallback = onEvent;

            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = watchPath;
            watcher.IncludeSubdirectories = true;
            
            // 该代码设置了文件系统监视器watcher的NotifyFilter属性，使其能够监控文件或目录的最后写入时间、文件名和目录名的变化。
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess;
            // 类比参考如下代码
            // watcher.Changed += OnChanged;
            // public static void OnChanged(object source, FileSystemEventArgs e)
            // {
            //      Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
            // }   
            watcher.Changed += new FileSystemEventHandler((object source, FileSystemEventArgs e) => { ProcessEvent(e, ChangeType.CHANGED); });
            watcher.Created += new FileSystemEventHandler((object source, FileSystemEventArgs e) => { ProcessEvent(e, ChangeType.CREATED); });
            watcher.Deleted += new FileSystemEventHandler((object source, FileSystemEventArgs e) => { ProcessEvent(e, ChangeType.DELETED); });
            watcher.Renamed += new RenamedEventHandler((object source, RenamedEventArgs e) => { ProcessEvent(e); });
            watcher.Error += new ErrorEventHandler((object source, ErrorEventArgs e) => { onError(e); });

            watcher.InternalBufferSize = 32768; // changing this to a higher value can lead into issues when watching UNC drives

            return watcher;
        }


        // 响应函数，调用参数 private Action<FileEvent> eventCallback
        // 调用参数 private Action<FileEvent> eventCallback时，没有直接调用，而是将其封装在ProcessEvent函数中
        private void ProcessEvent(FileSystemEventArgs e, ChangeType changeType)
        {
            this.eventCallback(new FileEvent
            {
                changeType = (int)changeType,
                path = e.FullPath
            });
        }

        // 单独处理改名事件的响应函数
        private void ProcessEvent(RenamedEventArgs e)
        {
            // 新路径是否在监控路径下
            var newInPath = e.FullPath.StartsWith(watchPath);
            // 旧路径是否在监控路径下
            var oldInPath = e.OldFullPath.StartsWith(watchPath);

            if (newInPath)
            {
                this.eventCallback(new FileEvent
                {
                    changeType = (int)ChangeType.CREATED,
                    path = e.FullPath
                });
            }

            if (oldInPath)
            {
                this.eventCallback(new FileEvent
                {
                    changeType = (int)ChangeType.DELETED,
                    path = e.OldFullPath
                });
            }
        }
    }
}